package com.googlecode.mailnet.netmodel;

import com.googlecode.mailnet.math.distribution.Distribution;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 * Unidirectional network link.
 *
 * @author Iskander Akishev
 */
public class Link extends NetworkItem implements Serializable {

	public Node getSource() {
		return source;
	}

	public Node getDestination() {
		return destination;
	}

	public Distribution getCapacityDistribution() {
		return capacityDistribution;
	}

	public void setCapacityDistribution(Distribution capacityDistribution) {
		checkBeforeModifications();
		this.capacityDistribution = capacityDistribution;
	}

	public double getPackageCorruptionProbability() {
		return packageCorruptionProbability;
	}

	public void setPackageCorruptionProbability(double packageCorruptionProbability) {
		checkBeforeModifications();
		this.packageCorruptionProbability = packageCorruptionProbability;
	}

	public int getDelay() {
		return delay;
	}

	public void setDelay(int delay) {
		checkBeforeModifications();
		this.delay = delay;
	}

	public double getFailureProbability() {
		return failureProbability;
	}

	public void setFailureProbability(double failureProbability) {
		networkConfiguration.checkBeforeModifications();
		this.failureProbability = failureProbability;
	}

	public Distribution getFailureDurationDistribution() {
		return failureDurationDistribution;
	}

	public void setFailureDurationDistribution(Distribution failureDurationDistribution) {
		networkConfiguration.checkBeforeModifications();
		this.failureDurationDistribution = failureDurationDistribution;
	}

	public void remove() {
		if (markRemoved()) {
			return;
		}
		boolean result = networkConfiguration.links.remove(this);
		assert result;

		for (Route route : usingRoutes.toArray(new Route[usingRoutes.size()])) {
			route.remove();
		}
		
		result = source.outgoingLinks.remove(this);
		assert result;
		result = destination.incomingLinks.remove(this);
		assert result;
	}

	// ==================== Implementation ====================

	private final Node source;
	private final Node destination;

	private Distribution capacityDistribution;
	private double packageCorruptionProbability;
	private int delay;

	private double failureProbability;
	private Distribution failureDurationDistribution;
	
	Set<Route> usingRoutes = new HashSet<Route>();

	Link(NetworkConfiguration networkConfiguration, Node source, Node destination) {
		super(networkConfiguration);

		if ((source.networkConfiguration != networkConfiguration)
			|| (destination.networkConfiguration != networkConfiguration))
		{
			throw new IllegalArgumentException("Nodes belong to another network");
		}
		source.checkBeforeModifications();
		destination.checkBeforeModifications();
		source.outgoingLinks.add(this);
		destination.incomingLinks.add(this);
		this.source = source;
		this.destination = destination;

		networkConfiguration.links.add(this);
	}

	void validate() throws IllegalStateException {
		assert !isRemoved() && !source.isRemoved() && !destination.isRemoved();
		if (capacityDistribution == null) {
			throw new IllegalStateException("Capacity distribution undefined for link \"" + this + "\"");
		}
		if (failureDurationDistribution == null) {
			throw new IllegalStateException("Failure duration distribution undefined for link \"" + this + "\"");
		}
	}
}
